#include <libriscv/machine.hpp>
#include <libriscv/debug.hpp>
#include <cassert>
using namespace riscv;

unsigned char crash_f6999f60cd85cb2a4b567e2e9783c63001de98f0[] = {
  0x33, 0xce, 0x36, 0x36, 0xc7, 0xed, 0x00, 0xb3, 0x33, 0xce, 0x00, 0xb3,
  0x33, 0xce, 0x00, 0xb3, 0x33, 0xce, 0x36, 0x36, 0x00, 0x01, 0xc7, 0xed,
  0x00, 0xb3, 0x33, 0xce, 0x00, 0xb3, 0x33, 0xce, 0x36, 0x36, 0x13, 0x5d,
  0x06, 0x00, 0x06, 0x00, 0x00, 0x01, 0xc7, 0xed, 0x00, 0xb3, 0x33, 0xce,
  0x00, 0xb3, 0x33, 0xce, 0x00, 0xb3, 0x33, 0xce, 0x36, 0x36, 0xc7, 0xed,
  0x00, 0xb3, 0x33, 0xce, 0x00, 0xb3, 0x33, 0xce, 0x00, 0xb3, 0x33, 0xce,
  0x36, 0x36, 0x00, 0x01, 0xc7, 0xed, 0x00, 0xb3, 0x33, 0xce, 0x00, 0xb3,
  0x33, 0xce, 0x36, 0x36, 0xc7, 0xed, 0x00, 0xb3, 0x33, 0xce, 0x08, 0xb3,
  0x33, 0xce, 0x00, 0xb3, 0x33, 0xce
};
unsigned int crash_f6999f60cd85cb2a4b567e2e9783c63001de98f0_len = 102;

unsigned char crash_675b93f2255f0ac4ca4ae13f4e9f8122d74baea8[] = {
  0x33, 0xd8, 0xbf, 0x40, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x2e, 0x2e,
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x5d, 0x2e, 0x2e,
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x26, 0x2e, 0x2e,
  0x2e, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x06, 0x10, 0x00, 0x00, 0x7a, 0x7a,
  0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x53, 0x74, 0x39, 0x65, 0x78,
  0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4f
};
unsigned int crash_675b93f2255f0ac4ca4ae13f4e9f8122d74baea8_len = 92;

unsigned char crash_983d2079843182f2cb27e6aeeb47af256c44fcdd[] = {
  0x33, 0x6e, 0x0a, 0x33, 0x33, 0x6e, 0x5b, 0x8a, 0x6e, 0x8a, 0x8a, 0x8a,
  0xcd
};
unsigned int crash_983d2079843182f2cb27e6aeeb47af256c44fcdd_len = 13;

unsigned char timeout_1a6dbaa717f8837c4bd4332121e92bd73bbec049[] = {
  0xcf
};
unsigned int timeout_1a6dbaa717f8837c4bd4332121e92bd73bbec049_len = 1;

template <int W>
void execute(uint64_t max_mem, const char* array_name,
			uint8_t* data, size_t len)
{
	riscv::Machine<W> machine { std::string_view{}, {
		.memory_max = max_mem
	} };
	printf("* Testing %s\n", array_name);

	for (size_t i = 0; i < 2; i++)
	{
		machine.memory.evict_execute_segments();
		try
		{
			// Make available on machine
			machine.cpu.init_execute_area(data, 0x1000, len);
			auto &seg = machine.cpu.current_execute_segment();

			// Also, make the instructions readable & executable
			machine.copy_to_guest(0x1000, data, len);
			machine.memory.set_page_attr(0x1000, riscv::Page::size(), {
				.read = true, .write = false, .exec = true
			});
			machine.cpu.jump(0x1000);
			// let's avoid loops

			DebugMachine<W> debug { machine };

			debug.verbose_instructions = true;
			debug.simulate(5000);
		}
		catch (std::exception &e)
		{
			//printf(">>> Exception: %s\n", e.what());
		}
	}
}

#define TEST_CRASH(m, x) execute<m>(65536ul, #x, x, sizeof(x))

void test_crashes()
{
	// test for crashes
	TEST_CRASH(4, crash_f6999f60cd85cb2a4b567e2e9783c63001de98f0);
	TEST_CRASH(8, crash_f6999f60cd85cb2a4b567e2e9783c63001de98f0);
	TEST_CRASH(4, crash_675b93f2255f0ac4ca4ae13f4e9f8122d74baea8);
	TEST_CRASH(8, crash_675b93f2255f0ac4ca4ae13f4e9f8122d74baea8);
	TEST_CRASH(4, crash_983d2079843182f2cb27e6aeeb47af256c44fcdd);
	TEST_CRASH(8, crash_983d2079843182f2cb27e6aeeb47af256c44fcdd);
	// test for timeout
	TEST_CRASH(4, timeout_1a6dbaa717f8837c4bd4332121e92bd73bbec049);
	TEST_CRASH(8, timeout_1a6dbaa717f8837c4bd4332121e92bd73bbec049);
}
